package springinpractice.ch8.mailinglist.web;

import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import springinpractice.ch8.mailinglist.model.Subscriber;
import springinpractice.ch8.mailinglist.model.Unsubscriber;
import springinpractice.ch8.mailinglist.service.ConfirmationExpiredException;
import springinpractice.ch8.mailinglist.service.ConfirmationFailedException;
import springinpractice.ch8.mailinglist.service.MailingListService;
import springinpractice.common.util.ValidationUtil;

/**
 * @author Willie Wheeler and John Wheeler
 * Book         :       'Spring In Practice' [Book written by Willie Wheeler and John Wheeler]
 **/

@RequestMapping(value={"/mailinglist/*"})
@Controller
public final class MailingListController {
	private static final String SUBSCRIBER = "subscriber";
	private static final String UNSUBSCRIBER = "unsubscriber";
	
	private MailingListService mailingListService;
	private Validator validator;
	
	private String subscribeViewName;
	private String subscribePreconfirmViewName;
	private String subscribeSuccessViewName;
	private String unsubscribeViewName;
	private String unsubscribePreconfirmViewName;
	private String unsubscribeSuccessViewName;
	
	
	// ========================================================================
	// Dependency injection methods
	// ========================================================================
	
	@Required
	public void setMailingListService(MailingListService mailingListService) {
		this.mailingListService = mailingListService;
	}
	
	@Required
	public void setValidator(Validator validator) {
		this.validator = validator;
	}
	
	@Required
	public void setSubscribeViewName(String viewName) {
		this.subscribeViewName = viewName;
	}
	
	@Required
	public void setSubscribePreconfirmViewName(String viewName) {
		this.subscribePreconfirmViewName = viewName;
	}
	
	@Required
	public void setSubscribeSuccessViewName(String viewName) {
		this.subscribeSuccessViewName = viewName;
	}
	
	@Required
	public void setUnsubscribeViewName(String viewName) {
		this.unsubscribeViewName = viewName;
	}
	
	@Required
	public void setUnsubscribePreconfirmViewName(String viewName) {
		this.unsubscribePreconfirmViewName = viewName;
	}
	
	@Required
	public void setUnsubscribeSuccessViewName(String viewName) {
		this.unsubscribeSuccessViewName = viewName;
	}
	
	
	// ========================================================================
	// Init binders
	// ========================================================================

	@InitBinder(SUBSCRIBER)
	public void initSubscriberBinder(WebDataBinder binder) {
		binder.setAllowedFields(new String[] {
			"firstName", "lastName", "email"
		});
	}
	
	@InitBinder(UNSUBSCRIBER)
	public void initUnsubscriberBinder(WebDataBinder binder) {
		binder.setAllowedFields(new String[] { "email" });
	}
	
	
	// ========================================================================
	// Subscription service methods
	// ========================================================================
	
	@RequestMapping(value = "/mailinglist/subscribe.do", method = RequestMethod.GET)
	public void subscribe(Model model) {
		model.addAttribute(new Subscriber());
	}
	
	@RequestMapping(value = "/mailinglist/subscribe.do", method = RequestMethod.POST)
	public String subscribe(
			HttpServletRequest request,
			@ModelAttribute(SUBSCRIBER) Subscriber subscriber,
			BindingResult result) {
		
		ValidationUtil.verifyBinding(result);
		
		validator.validate(subscriber, result);
		if (result.hasErrors()) {
			result.reject("global.error");
			return null;
		}
		
		// Create the unconfirmed subscriber. Confirmation is a separate step.
		// This will allow us to include a subscriber ID in the generated e-mail
		// instead of having include the first name, last name and e-mail in
		// the link we put in the e-mail.
		subscriber.setIpAddress(request.getRemoteAddr());
		subscriber.setDateCreated(new Date());
		mailingListService.addSubscriber(subscriber);
		return subscribePreconfirmViewName;
	}
	
	@RequestMapping("/mailinglist/subscribe-preconfirm.do")
	public void preconfirmSubscription() {
	}
	
	@RequestMapping("/mailinglist/subscribe-confirm.do")
	public String confirmSubscription(
			@RequestParam("s") long subscriberId,
			@RequestParam("d") String digest,
			Model model) {
		
		try {
			mailingListService.confirmSubscriber(subscriberId, digest);
			return subscribeSuccessViewName;
		} catch (ConfirmationExpiredException e) {
			model.addAttribute("expired", true);
			return subscribeViewName;
		} catch (ConfirmationFailedException e) {
			model.addAttribute("failed", true);
			return subscribeViewName;
		}
	}
	
	
	// ========================================================================
	// Unsubscription service methods
	// ========================================================================
	
	@RequestMapping(value = "/mailinglist/unsubscribe.do", method = RequestMethod.GET)
	public void unsubscribe(Model model) {
		model.addAttribute(new Unsubscriber());
	}
	
	@RequestMapping(value = "/mailinglist/unsubscribe.do", method = RequestMethod.POST)
	public String unsubscribe(
			@ModelAttribute(UNSUBSCRIBER) Unsubscriber unsubscriber,
			BindingResult result) {
		
		ValidationUtil.verifyBinding(result);
		validator.validate(unsubscriber, result);
		if (result.hasErrors()) {
			result.reject("global.error");
			return null;
		}
		
		mailingListService.requestUnsubscription(unsubscriber.getEmail());
		return unsubscribePreconfirmViewName;
	}
	
	@RequestMapping("/mailinglist/unsubscribe-preconfirm.do")
	public void preconfirmUnsubscription() {
	}
	
	@RequestMapping("/mailinglist/unsubscribe-confirm.do")
	public String confirmUnsubscription(
			@RequestParam("e") String email,
			@RequestParam("t") long timestamp,
			@RequestParam("d") String digest,
			Model model) {
		
		try {
			mailingListService.confirmUnsubscription(email, timestamp, digest);
			return unsubscribeSuccessViewName;
		} catch (ConfirmationExpiredException e) {
			// Model attributes exposed as HTTP params for RedirectView
			model.addAttribute("expired", true);
			return unsubscribeViewName;
		} catch (ConfirmationFailedException e) {
			// Model attributes exposed as HTTP params for RedirectView
			model.addAttribute("failed", true);
			return unsubscribeViewName;
		}
	}
}
